﻿using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
using Myotragus.Data.Util;

namespace Myotragus.Data.Emit
{
	public class DynamicAssembly : IDynamicAssembly
	{
		public DynamicAssembly(INameGenerator nameGenerator)
		{
			NameGenerator = nameGenerator;
			MainNamespace = NameGenerator.CreateAssemblyName();
			Assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(
				new AssemblyName(MainNamespace),
				AssemblyBuilderAccess.RunAndSave);
			Module = Assembly.DefineDynamicModule(MainNamespace + ".dll");

			AppDomain.CurrentDomain.AssemblyResolve += CurrentDomainAssemblyResolve;
		}

		protected INameGenerator NameGenerator
		{
			get;
			private set;
		}

		~DynamicAssembly()
		{
			Dispose();
		}

		private Assembly CurrentDomainAssemblyResolve(object sender, ResolveEventArgs args)
		{
			return args.Name == Assembly.ToString() ? Assembly : null;
		}

		public string MainNamespace
		{
			get;
			protected set;
		}

		public AssemblyBuilder Assembly
		{
			get;
			protected set;
		}

		public ModuleBuilder Module
		{
			get;
			protected set;
		}

		public IDynamicType DefineType(string typeName, TypeAttributes attributes, 
			Type baseType, IEnumerable<Type> interfaces)
		{
			if (typeName == null)
				throw new ArgumentNullException("typeName");
			if (baseType == null)
				throw new ArgumentNullException("baseType");

			interfaces = interfaces ?? new Type[0];

			var result = new DynamicType(NameGenerator, Module, typeName, attributes, baseType, interfaces);

			return result;
		}

		#region IDisposable Members

		public void Dispose()
		{
			AppDomain.CurrentDomain.AssemblyResolve -= CurrentDomainAssemblyResolve;
		}

		#endregion IDisposable Members
	}
}